/*INCLUDES *******************************************************************/

#include "ses_button.h"
#include "ses_buzzer.h"
#include "ses_lcd.h"
#include "ses_lcdDriver.h"
#include "ses_led.h"
#include "ses_scheduler.h"
#include "ses_alarmclock.h"
#include "ses_timer.h"

/*MACROS************************************************************************/

#define SEC   		1000
#define EXPIRERED   500				// RED LED expire time
#define PERIODRED   500				// RED LED period time
/*GLOABLE VARIABLE**************************************************************/

volatile uint8_t time_mode = 0;
volatile uint8_t flag = 0;
volatile uint32_t alarmtime = 0;

/*Signal used by the clock******************************************************/

enum {
	BEGIN = 0, HOUR, MINUTE, FINISH
};

/*FUNCTION DEFINITION ***********************************************************/

/* dispatches events to state machine, called in application*/
inline static void fsm_dispatch(Fsm* fsm, const Event* event) {
	fsm->state(fsm, event);
}
;

/* sets and calls initial state of state machine */
inline static void fsm_init(Fsm* fsm, State init) {
	fsm->state = init;
	fsm_dispatch(fsm, NULL );
}
;

/*
 * Finite State Machines (FSM)
 * dispatch and Initialization
 */
void fsmdispatch(Fsm* fsm, const Event* event) {
	fsm_dispatch(fsm, event);
}

void fsminit(Fsm* fsm, State init) {
	fsm_init(fsm, init);
}

/*
 * Alarm Clock initialization
 * LCD / Scheduler / Button / Timer2 / Buzzer / Leds
 * Enable interrupts
 * Set RTC
 */
void alarmclock_init(Fsm *fsm, const Event *event) {
	lcd_init();
	stdout=lcdout;
	scheduler_init();  //Initialize Scheduler.
	button_init();
	timer2_init(scheduler_update);
	buzz_Init();
	led_greenInit();
	led_redInit();
	led_yellowInit();
	led_redOff();
	led_greenOff();
	led_yellowOff();
	sei();
	(fsm->state) = alarmclock_setrtc;
}

/*
 * Clock setting up
 * by prompting the User to enter the Time
 */
void alarmclock_setrtc(Fsm *fsm, const Event *e) {
	lcd_setCursor(0, 1);
	fprintf(lcdout, "Please set time!    ");
	fsm->alarmSet = 0;
	settime(fsm, e);
}

/*
 * Set Alarm Clock to Normal by setting the state
 * check if signal is @ROTARY_BUTTON, then set fsm state to @setalarm
 */
void alarmclock_normal(Fsm *fsm, const Event *e) {
//display HH:MM:ss;
	displayRTC();
	fsm->alarmSet = 0;  //alarm disable.

	if (flag == 1) {
		switch ((e->signal)) {
		case ROTARY_BUTTON:
			flag = 0;
			fsm->state = alarmclock_setalarm;
			break;
		default:
			flag = 0;
			break;
		}
	}
}

/*
 * Set Alarm by @settime function by sending as arguments
 * Finite State machine @fsm / and the event @e
 */
void alarmclock_setalarm(Fsm *fsm, const Event *e) {
	fsm->alarmSet = 0;
	lcd_setCursor(0, 2);
	fprintf(lcdout, "Set alarm time! ");
	displayRTC();
	fsm->alarmSet = 0;
	settime(fsm, e);
}

/*
 * Turn the Alarm Clock if
 * @TIME_OUT: Buzzer-On / set FSM-state = @alarm clock buzz
 * @ROTARY_BUTTON : Reset Alarm Time / set FSM-state = @alarm clock normal
 */
void alarmclock_alarmon(Fsm *fsm, const Event *e) {
	fsm->alarmSet = 1;  //alarm on.
	fsm->timeSet = alarmtime;

	lcd_setCursor(0, 2);
	fprintf(lcdout, "                       ");

	displayRTC();
	if (flag == 1) {
		switch ((e->signal)) {
		case ROTARY_BUTTON:
			flag = 0;
			led_yellowOff();
			lcd_clear();
			fsm->timeSet = 0;
			fsm->state = alarmclock_normal;
			break;
		case TIME_OUT:
			flag = 0;
			//Turn Yellow Led OFF
			led_yellowOff();
			//blink Red Led
			scheduler_add(led_redToggle, EXPIRERED, PERIODRED);
			//Turn Buzzer On
			buzz_Act();
			fsm->state = alarmclock_buzz;
			break;
		default:
			flag = 0;
			break;
		}
	}
}

/*
 * Alarm Clock Buzz is used to check if Event Signal is @WAIT_30s
 *  then it:
 *  deactivates Buzzer / remove Red Led toggling / Clock mode to Normal
 */
void alarmclock_buzz(Fsm *fsm, const Event *e) {
	fsm->alarmSet = 0;
	displayRTC();
	if (flag == 1) {
		switch ((e->signal)) {
		case JOYSTICK_BUTTON:
		case ROTARY_BUTTON:
		case WAIT_30S:
			flag = 0;
			buzz_Deact();
			scheduler_remove(led_redToggle);
			led_redOff();
			lcd_clear();
			fsm->state = alarmclock_normal;
			break;
		}
	}
}

void changemode() {
	time_mode++;
}

/*
 * Function used to Set Hour/Min
 * by incrementing Hour or Min depending on the Case we are at
 * @return the set time (Hour/Mi)
 */
uint32_t set_hourmin(uint32_t abcde) {
	static uint8_t hour = 0, minute = 0;
	switch (time_mode) {
	case BEGIN:
		break;
	case HOUR:
		hour++;
		abcde += HOUR_MS;
		if (hour == 24) {
			//check if we exceeded 24 Hours, if yes then reset
			abcde -= (hour * HOUR_MS );
			hour = 0;
		}
		break;
	case MINUTE:
		minute++;
		abcde += MIN_MS;
		if (minute == 60) {
			//check if we exceeded 60 Mins, if yes then reset
			abcde -= (minute * MIN_MS );
			minute = 0;
		}
		break;
	case FINISH:
		break;
	}
	return abcde;
}

void setflag() {
	flag = 1;
}

/*
 * Function responsible for printing RTC on LCD
 */
void displayRTC() {
	struct type_timeRTC realtime;
	//get Time and save it at @realtime variable
	scheduler_getTimeRTC(&realtime);
	lcd_setCursor(0, 1);
	printf("      %d:%d:%d     ", (realtime.hour), (realtime.minute),
			(realtime.second));
}

/*
 * Function @settime used to set time using @tempsettime variable
 * after converting it from @timeset structure
 * It checks which Time to set by checking Event Signal
 * if Joystick -> increments Hour/Min
 * if Rotary -> flip between setting Time clock or Alarm clock
 */
void settime(Fsm *fsm, const Event *e) {
	static volatile uint32_t tempsettime;
	struct type_timeRTC timeset;
	ms_convertTime(tempsettime, &timeset);
	lcd_setCursor(0, 0);
	printf("       %d:%d       ", (timeset.hour), (timeset.minute));
	if (flag == 1) {
		//check Events (signal): Joystick / Rotary
		switch ((e->signal)) {
		case JOYSTICK_BUTTON:
			flag = 0;
			changemode();
			if (time_mode == FINISH) {
				time_mode = BEGIN;
				if ((fsm->state) == alarmclock_setrtc) {
					scheduler_setTime(tempsettime);
					tempsettime = 0;
					lcd_clear();
					scheduler_add(led_greenToggle, SEC, SEC);
					fsm->state = alarmclock_normal;
				} else if ((fsm->state) == alarmclock_setalarm) {
					alarmtime = tempsettime;
					tempsettime = 0;
					led_yellowOn();
					fsm->state = alarmclock_alarmon;
				}
			}
			break;
		case ROTARY_BUTTON:
			flag = 0;
			tempsettime = set_hourmin(tempsettime);

			break;
		}
	}
}
